package com.fezs.redis.aop;

import cn.hutool.extra.servlet.ServletUtil;
import com.fezs.common.constants.CacheConstant;
import com.fezs.common.exception.BusinessException;
import com.fezs.redis.annotation.RedissonLimiter;
import com.fezs.redis.enums.LimitType;
import com.fezs.redis.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RateType;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * 限流处理
 */
@Slf4j
@Aspect
public class RedissonLimiterAop {

    @Before("@annotation(rateLimiter)")
    public void doBefore(JoinPoint point, RedissonLimiter rateLimiter) throws Throwable {
        int time = rateLimiter.time();
        int count = rateLimiter.count();
        String msg = rateLimiter.msg();

        String combineKey = getCombineKey(rateLimiter, point);
        try {
            RateType rateType = rateLimiter.limitType() == LimitType.CLUSTER ? RateType.PER_CLIENT : RateType.OVERALL;
            long number = RedisUtils.rateLimiter(combineKey, rateType, count, time);
            if (number == -1) {
                throw new BusinessException(msg);
            }
            log.info("缓存key => '{}', 限制令牌 => {}, 剩余令牌 => {}", combineKey, count, number);
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException("服务器限流异常，请稍候再试!");
        }
    }

    public String getCombineKey(RedissonLimiter rateLimiter, JoinPoint point) {
        StringBuilder sb = new StringBuilder(CacheConstant.RATE_LIMIT);
        sb.append(":");
        if (rateLimiter.limitType() == LimitType.IP) {
            // 获取请求ip
            sb.append(this.getClientIP()).append("-");
        } else if (rateLimiter.limitType() == LimitType.CLUSTER) {
            // 获取客户端实例id
            sb.append(RedisUtils.getClient().getId()).append("-");
        }
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        sb.append(method.getDeclaringClass().getSimpleName()).append(".").append(method.getName());
        return sb.toString();
    }

    /**
     * 获得请求
     */
    public HttpServletRequest getRequest() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (!(requestAttributes instanceof ServletRequestAttributes)) {
            return null;
        }
        return ((ServletRequestAttributes) requestAttributes).getRequest();
    }

    public String getClientIP() {
        HttpServletRequest request = getRequest();
        if (request == null) {
            return "";
        }
        return ServletUtil.getClientIP(request);
    }
}
